﻿using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Sgr.Domain.Uow;
using Sgr.Trackers.Domain;

namespace Sgr.Trackers.BackGroundTasks
{
    public class MessageTrackerLogCleanupHandle : ICleanupHandle
    {
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        private readonly IMessageTrackerLogRepository _messageTrackerLogRepository;
        private readonly TrackerOptions _trackerOptions;
        private readonly ILogger<MessageTrackerLogCleanupHandle> _logger;

        public MessageTrackerLogCleanupHandle(
            IUnitOfWorkManager unitOfWorkManager,
            IMessageTrackerLogRepository messageTrackerLogRepository,
            IOptions<TrackerOptions> options,
            ILogger<MessageTrackerLogCleanupHandle> logger)
        {
            _unitOfWorkManager = unitOfWorkManager ?? throw new ArgumentNullException(nameof(unitOfWorkManager));
            _messageTrackerLogRepository = messageTrackerLogRepository ?? throw new ArgumentNullException(nameof(messageTrackerLogRepository));
            _trackerOptions = options?.Value ?? throw new ArgumentNullException(nameof(options));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        /// <summary>
        /// 执行幂等记录表的清理任务。
        /// 清理早于保留天数的已处理消息记录（ProcessedOnUtc 小于 当前时间 - 保留天数），
        /// 按批次删除，每次最多删除 MaxBatchSize 条，最多执行 MaxCycles 次。
        /// 整个过程包裹在事务中，确保安全。
        /// </summary>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>一个任务，表示清理操作的异步执行</returns>
        public async Task CleanupAsync(CancellationToken cancellationToken = default)
        {
            _logger.LogInformation("MessageTrackerLogCleanupHandle 于 {UtcNow} 开始清理幂等记录表数据", DateTime.UtcNow);

            // 直接复用通用的清理逻辑，传入清理已处理消息的方法
            var count = await CleanupMessagesAsync(
                _messageTrackerLogRepository.CleanupAsync,
                _trackerOptions.ProcessedMessageRetentionPeriod,
                "幂等记录（已处理）",
                cancellationToken);

            _logger.LogInformation("MessageTrackerLogCleanupHandle 幂等记录表清理完成，共计清理 {Count} 条记录", count);
        }

        /// <summary>
        /// 通用的清理方法，用于分批次、事务安全地删除早于指定时间的记录。
        /// </summary>
        /// <param name="cleanupFunc">
        /// 清理方法委托，接收 olderThanUtc 和 max，返回删除的行数。
        /// 通常由具体的 Repository 提供，如 CleanupProcessedMessagesAsync。
        /// </param>
        /// <param name="retentionDays">保留天数，超过该天数的数据将被删除</param>
        /// <param name="messageType">用于日志记录的消息类型描述，如“幂等记录（已处理）”</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>实际删除的总行数</returns>
        private async Task<int> CleanupMessagesAsync(
            Func<DateTime, int, CancellationToken, Task<int>> cleanupFunc,
            int retentionDays,
            string messageType,
            CancellationToken cancellationToken = default)
        {
            int totalAffected = 0;
            var maxBatchSize = _trackerOptions.TrackerCleanupMaxBatchSize;

            // 限制批次大小范围，避免一次删太多
            if (maxBatchSize < 10)
                maxBatchSize = 10;
            if (maxBatchSize > 2000)
                maxBatchSize = 2000;

            var maxCycles = _trackerOptions.TrackerCleanupMaxCycles;

            // 限制最大循环次数，避免长时间运行
            if (maxCycles < 5)
                maxCycles = 5;
            if (maxCycles > 500)
                maxCycles = 500;

            var cleanupStartTime = DateTime.UtcNow;

            for (int i = 0; i < maxCycles; i++)
            {
                using var uow = _unitOfWorkManager.Begin();
                try
                {
                    // 计算清理阈值：当前时间 - 保留天数
                    var olderThanUtc = cleanupStartTime.AddDays(-retentionDays);
                    int affected = await cleanupFunc(olderThanUtc, maxBatchSize, cancellationToken);

                    await uow.CommitAsync(cancellationToken);

                    totalAffected += affected;

                    // 如果本次删除的条数小于批次大小，说明没有更多数据，提前退出
                    if (affected < maxBatchSize)
                        break;
                }
                catch (Exception ex)
                {
                    await uow.RollbackAsync(cancellationToken);
                    _logger.LogError(ex, "MessageTrackerLogCleanupHandle 清理{MessageType}时发生异常，终止清理", messageType);
                    break; // 出错即停止，避免脏数据或部分删除
                }
            }

            return totalAffected;
        }
    }
}